package felix.things.router.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import felix.things.router.opc.conf.identity.KeystoreConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.X509Certificate;

@Slf4j
public class ConfigurationTools {

    private static final ObjectMapper mapper = new ObjectMapper();

    public static <T> T readConfiguration(JsonNode configurationNode, Class<T> clazz) throws IOException {
        try {
            return mapper.treeToValue(configurationNode, clazz);
        } catch (IOException e) {
            log.error("Failed to load {} configuration from {}", clazz, configurationNode);
            throw e;
        }
    }

    public static <T> T readFileConfiguration(String configurationFile, Class<T> clazz) throws IOException {
        try {
            return mapper.readValue(getFileAsStream(configurationFile), clazz);
        } catch (IOException e) {
            log.error("Failed to load {} configuration from {}", clazz, configurationFile);
            throw e;
        }
    }

    public static CertificateInfo loadCertificate(KeystoreConfiguration configuration) throws GeneralSecurityException, IOException {
        try {
            KeyStore keyStore = KeyStore.getInstance(configuration.getType());
            keyStore.load(getFileAsStream(configuration.getLocation()), configuration.getPassword().toCharArray());
            Key key = keyStore.getKey(configuration.getAlias(), configuration.getKeyPassword().toCharArray());
            if (key instanceof PrivateKey) {
                X509Certificate certificate = (X509Certificate) keyStore.getCertificate(configuration.getAlias());
                PublicKey publicKey = certificate.getPublicKey();
                KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);
                return new CertificateInfo(certificate, keyPair);
            } else {
                throw new GeneralSecurityException(configuration.getAlias() + " is not a private key!");
            }
        } catch (IOException | GeneralSecurityException e) {
            log.error("Keystore configuration: 【{}】 is invalid!", configuration, e);
            throw e;
        }
    }

    private static InputStream getResourceAsStream(String fileContent) {
        byte[] decoded = Base64.decodeBase64(fileContent);
        return new ByteArrayInputStream(decoded);
    }

    private static InputStream getFileAsStream(String configurationFile) {
        return ConfigurationTools.class.getClassLoader().getResourceAsStream(configurationFile);
    }
}
